package com.hy.servlet;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/*
loadOnStartup : -1 或不写 表示 只有访问该Servlet的时候，才会生成这个类的对象（调用构造方法），并调用init方法初始化
              ：写了loadOnStartup 并且值为 正数，表示容器启动就会生成这个类的对象（调用构造方法），并调用init方法初始化
                    如果有多个Servlet，并且 都配置了loadOnStartup 都是正数 ，比如：1,2
                    那么值越小越先启动。

 刚刚为什么会出现值为-1，但是 启动初始化操作？？？
 1)@WebServlet(value="/*") ，拦截所有请求
 2)IDEA里面配的tomcat一启动，就调用关联的浏览器，并且访问 http://localhost:8080/demo04
 */
@WebServlet(value = "/",loadOnStartup = 1)
public class DispatcherServlet extends HttpServlet {
    //创建一个bean容器 ，用来存放XxxContrller的对象
    private Map<String,Object> beanMap = new ConcurrentHashMap<>();
    private ObjectMapper objectMapper = new ObjectMapper();

    //通过java代码 ， 解析applicationContext.xml文档 当成html文档
    @Override
    public void init() throws ServletException {
        System.out.println("===============init============");
    }

    public DispatcherServlet() {
        //1，加载这个文档，要用流
        //得到类对象.得到这个类对象的类加载器
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("applicationContext.xml");

        //在html中，js 中可以直接访问 document对象，内置的对象
        // xml 相当于 html

        //2,通过工厂模式，创建documentBuilderFactory工厂对象
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();

        try {
            // 3. 通过工厂对象，创建DocumentBuilder对象
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();

            // document对象，代表整个 applicationContext 文档
            Document document = documentBuilder.parse(inputStream);

            NodeList nodeList = document.getElementsByTagName("bean");

//            System.out.println(nodeList.getLength());

            for(int i=0,len= nodeList.getLength();i<len;i++){
                Node node = nodeList.item(i);

                //Node类中没有提供更好的方法用来获取id，class属性
                Element element = (Element) node;

                String id = element.getAttribute("id");
                String className = element.getAttribute("class");

                //                System.out.println(id+","+className);
                //通过反射机制来创建对象

                if(beanMap.containsKey(id))
                    continue;

                Class clazz = Class.forName(className);

                Object xxxController = clazz.newInstance();

                beanMap.put(id,xxxController);
            }
        } catch (ParserConfigurationException | SAXException | IOException | ClassNotFoundException |
                 InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");

        //http://localhost:8080/demo03/emp
        // 思路是：
        // 第1步： /demo03/emp ->   emp
        // 第2步： emp -> EmpController

        // 以前的 CoreServlet -> Empdao -> 数据库
        // 现在的 DispatcherServlet ->  EmpController -> -> 数据库
        //        DispatcherServlet ->  DeptController ->DEmpDaoeptDao -> 数据库
        // emp - > EmpContrller
        String requestURI = request.getRequestURI();
//        System.out.println("=====>"+requestURI);

        int contextPathLen = request.getContextPath().length();
        String newRequestURI = requestURI.substring(contextPathLen+1);

        /*
        System.out.println("requestURI = " + requestURI); //  /demo042/emp/admin
        int lastIndexOf = requestURI.lastIndexOf("/");
        String newRequestURI = requestURI.substring(lastIndexOf +1);
        */

//        System.out.println("newRequestURI =" + newRequestURI); // admin


        //4.3 通过requestURI获取对应的xxxController对象
        //访问路径 : http://localhost:8080/demo04/emp?ac=list

        Object xxxController = beanMap.get(newRequestURI); //  /back/admin

//        System.out.println("xxxController=========="+xxxController);

        if(xxxController==null)
            return;

        String ac = request.getParameter("ac");  // list

        if(StringUtils.isBlank(ac))
            return;

        Method[] methods = xxxController.getClass().getMethods();

        for(Method method : methods){
            try {
                if(ac.equals(method.getName())){
                    //如果ac传过来的名字 和 method名字相同

                    //就算方法中没有参数，parameters也不为null，对象存在 parameters.length 为0
                    Parameter[] parameters = method.getParameters();

                    int length = parameters.length;

                    //System.out.println("dispatcher = "+parameters.length);

                    //                    String arg0 = request.getParameter("arg0");
                    //                    String arg1 = request.getParameter("arg1");

                    Object[] parameterObjs = new Object[length];

                    for(int i=0,len=parameters.length;i<len;i++){
                        //                        System.out.println("dispatcher..."+parameters[i].getName());
                        //                        parameterObjs[i] = request.getParameter(parameters[i].getName());

                        //                        System.out.println(parameters[i].getType().getName());
//                        System.out.println(parameters[i].getType().getName());
                        if(parameters[i].getType() == Integer.class){
                            //要将字符串转成Integer类型
                            parameterObjs[i] = Integer.parseInt(request.getParameter(parameters[i].getName()));
                        }else if("int".equals(parameters[i].getType().getName())){
                            parameterObjs[i] = Integer.parseInt(request.getParameter(parameters[i].getName()));
                        }else if(parameters[i].getType() == java.lang.String.class){
                            //String类型 可以直接方法
                            parameterObjs[i] = request.getParameter(parameters[i].getName());
                        }else if(parameters[i].getType() == java.lang.Long.class){
                            parameterObjs[i] = Long.parseLong(request.getParameter(parameters[i].getName()));
                        }else if("long".equals(parameters[i].getType().getName())){
                            parameterObjs[i] = Long.parseLong(request.getParameter(parameters[i].getName()));
                        }else if( parameters[i].getType()== javax.servlet.http.HttpServletRequest.class){
                            parameterObjs[i] = request;
                        }else if( parameters[i].getType()== javax.servlet.http.HttpServletResponse.class){
                            parameterObjs[i] = response;
                        } else if( parameters[i].getType()== javax.servlet.http.HttpSession.class){
                            parameterObjs[i] = request.getSession(true);
                        }
                    }

                    //通过反射机制调用XxxContrller中的方法
                    //Object returnObj = method.invoke(xxxController, request, response);

                    Object returnObj = method.invoke(xxxController,parameterObjs);

                    //视图的跳转
                    if(returnObj !=null && returnObj.getClass() == String.class){
                        String returnStr = (String)returnObj;

                        if(returnStr.startsWith("redirect:")){
                            returnStr = returnStr.substring("redirect:".length());  //"/login.jsp"
                            response.sendRedirect(request.getContextPath()+ returnStr);
                            return;
                        }else if(returnStr.startsWith("forward:")){
                            returnStr = returnStr.substring("forward:".length());  //"/WEB-INF/index.jsp"
                            request.getRequestDispatcher(returnStr).forward(request, response);
                            return;
                        }
                    }else if(returnObj !=null && returnObj instanceof Object){
                        String returnJsonStr = objectMapper.writeValueAsString(returnObj);
                        PrintWriter out = response.getWriter();

                        out.write(returnJsonStr);
                        out.flush();
                        out.close();
                        return;
                    }
                }
            } catch (IllegalAccessException|InvocationTargetException e) {
                throw new RuntimeException(e);
            }
        }
    }


    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}
